#ifndef __INCLUDED_CBL_TUPLE_H__
#error "CBL_Tuple.hpp must not be used directly. Use CBL_Tuple.h instead."
#else
#ifndef __INCLUDED_CBL_TUPLE_HPP__
#define __INCLUDED_CBL_TUPLE_HPP__

#define TUPLE_TYPE CTuple<TType, TSize>

// ---------------------------------------------------------------------------------------------

template <typename TType, uint32 TSize>
TUPLE_TYPE::CTuple()
{ 
	memset(m_Values, 0, s_MemSize); 
}
		
// ---------------------------------------------------------------------------------------------

template <typename TType, uint32 TSize>
TUPLE_TYPE::CTuple(const TType* _pData)
{
	*this = _pData;
}
		
// ---------------------------------------------------------------------------------------------
		
template <typename TType, uint32 TSize>
template <typename TType2, uint32 TSize2>
TUPLE_TYPE::CTuple(const CTuple<TType2, TSize2>& _rOther)
{ 
	memset(m_Values, 0, s_MemSize); 
	*this = _rOther; 
}	
	
// ---------------------------------------------------------------------------------------------

template <typename TType, uint32 TSize>
CB_FORCEINLINE void CB_INLINE_ATTR TUPLE_TYPE::operator = (const TType* _pData)
{ 
	memcpy(m_Values, _pData, s_MemSize); 
}
	
// ---------------------------------------------------------------------------------------------
	
template <typename TType, uint32 TSize>
template<uint32 TSize2>
CB_FORCEINLINE void CB_INLINE_ATTR TUPLE_TYPE::operator = (const CTuple<TType, TSize2>& _rOther)
{ 
	uint32 MinSize = Math::Min(s_MemSize, _rOther.s_MemSize);
	memcpy(m_Values, _rOther.m_Values, MinSize);
}

// ---------------------------------------------------------------------------------------------
	
template <typename TType, uint32 TSize>
template <typename TType2, uint32 TSize2>
CB_FORCEINLINE void CB_INLINE_ATTR TUPLE_TYPE::operator = (const CTuple<TType2, TSize2>& _rOther)
{
	typedef Meta::Min<TSize,TSize2> MinSize;
	for (uint32 i(0u); i<MinSize::Result; ++i)
		m_Values[i] = static_cast<TType>(_rOther.m_Values);
}
	
// ---------------------------------------------------------------------------------------------
	
template <typename TType, uint32 TSize>
CB_FORCEINLINE TType& CB_INLINE_ATTR TUPLE_TYPE::operator [] (uint32 _Index)
{
	return m_Values[_Index];
}

// ---------------------------------------------------------------------------------------------

template <typename TType, uint32 TSize>
CB_FORCEINLINE const TType& CB_INLINE_ATTR TUPLE_TYPE::operator [] (uint32 _Index) const
{
	return m_Values[_Index];
}

// ---------------------------------------------------------------------------------------------

template <typename TType, uint32 TSize>
CB_FORCEINLINE TType* CB_INLINE_ATTR TUPLE_TYPE::Array()
{
	return m_Values;
}

// ---------------------------------------------------------------------------------------------

template <typename TType, uint32 TSize>
CB_FORCEINLINE const TType* CB_INLINE_ATTR TUPLE_TYPE::Array() const
{
	return m_Values;
}

// ---------------------------------------------------------------------------------------------

template <typename TType, uint32 TSize>
template <typename TType2, uint32 TSize2>
CB_FORCEINLINE TUPLE_TYPE& CB_INLINE_ATTR TUPLE_TYPE::operator *= (const CTuple<TType2, TSize2>& _rRhs)
{
	typedef Meta::Min<TSize,TSize2> MinSize;
	for (uint32 i(0u); i<MinSize::Result; ++i)
		m_Values[i] *= _rRhs[i];
	
	return *this;
}

// ---------------------------------------------------------------------------------------------

template <typename TType, uint32 TSize>
CB_FORCEINLINE TUPLE_TYPE& CB_INLINE_ATTR TUPLE_TYPE::operator *= (const TType& _rRhs)
{
	for (uint32 i(0u); i<TSize; ++i)
		m_Values[i] *= _rRhs;
	
	return *this;
}

// ---------------------------------------------------------------------------------------------

template <typename TType, uint32 TSize>
template <typename TType2, uint32 TSize2>
CB_FORCEINLINE TUPLE_TYPE& CB_INLINE_ATTR TUPLE_TYPE::operator /= (const CTuple<TType2, TSize2>& _rRhs)
{
	typedef Meta::Min<TSize,TSize2> MinSize;
	for (uint32 i(0u); i<MinSize::Result; ++i)
		m_Values[i] /= _rRhs[i];
	
	return *this;
}

// ---------------------------------------------------------------------------------------------

template <typename TType, uint32 TSize>
CB_FORCEINLINE TUPLE_TYPE& CB_INLINE_ATTR TUPLE_TYPE::operator /= (const TType& _rRhs)
{
	for (uint32 i(0u); i<TSize; ++i)
		m_Values[i] /= _rRhs;
	
	return *this;
}

// ---------------------------------------------------------------------------------------------

template <typename TType, uint32 TSize>
template <typename TType2, uint32 TSize2>
CB_FORCEINLINE TUPLE_TYPE& CB_INLINE_ATTR TUPLE_TYPE::operator += (const CTuple<TType2, TSize2>& _rRhs)
{
	typedef Meta::Min<TSize,TSize2> MinSize;
	for (uint32 i(0u); i<MinSize::Result; ++i)
		m_Values[i] += _rRhs[i];
	
	return *this;
}

// ---------------------------------------------------------------------------------------------

template <typename TType, uint32 TSize>
CB_FORCEINLINE TUPLE_TYPE& CB_INLINE_ATTR TUPLE_TYPE::operator += (const TType& _rRhs)
{
	for (uint32 i(0u); i<TSize; ++i)
		m_Values[i] += _rRhs;
	
	return *this;
}

// ---------------------------------------------------------------------------------------------

template <typename TType, uint32 TSize>
template <typename TType2, uint32 TSize2>
CB_FORCEINLINE TUPLE_TYPE& CB_INLINE_ATTR TUPLE_TYPE::operator -= (const CTuple<TType2, TSize2>& _rRhs)
{
	typedef Meta::Min<TSize,TSize2> MinSize;
	for (uint32 i(0u); i<MinSize::Result; ++i)
		m_Values[i] -= _rRhs[i];
	
	return *this;
}

// ---------------------------------------------------------------------------------------------

template <typename TType, uint32 TSize>
CB_FORCEINLINE TUPLE_TYPE& CB_INLINE_ATTR TUPLE_TYPE::operator -= (const TType& _rRhs)
{
	for (uint32 i(0u); i<TSize; ++i)
		m_Values[i] -= _rRhs;
	
	return *this;
}

// ---------------------------------------------------------------------------------------------

template <typename TType, uint32 TSize>
template <typename TType2, uint32 TSize2>
CB_FORCEINLINE CTuple<bool, TSize> CB_INLINE_ATTR TUPLE_TYPE::operator == (const CTuple<TType2, TSize2>& _rRhs) const
{
	CTuple<bool, TSize> Result;
	typedef Meta::Min<TSize,TSize2> MinSize;
	
	for (uint32 i(0u); i<MinSize::Result; ++i)
		Result[i] = (m_Values[i] == _rRhs[i]);
	
	for (uint32 i(MinSize::Result); i<TSize; ++i)
		Result[i] = false;
	
	return Result;
}
	
// ---------------------------------------------------------------------------------------------

template <typename TType, uint32 TSize>
template <typename TType2, uint32 TSize2>
CB_FORCEINLINE CTuple<bool, TSize> CB_INLINE_ATTR TUPLE_TYPE::operator != (const CTuple<TType2, TSize2>& _rRhs) const
{
	CTuple<bool, TSize> Result;
	typedef Meta::Min<TSize,TSize2> MinSize;
	
	for (uint32 i(0u); i<MinSize::Result; ++i)
		Result[i] = (m_Values[i] != _rRhs[i]);
	
	for (uint32 i(MinSize::Result); i<TSize; ++i)
		Result[i] = true;
	
	return Result;
}
	
// ---------------------------------------------------------------------------------------------

template <typename TType, uint32 TSize>
template <typename TType2, uint32 TSize2>
CB_FORCEINLINE CTuple<bool, TSize> CB_INLINE_ATTR TUPLE_TYPE::operator < (const CTuple<TType2, TSize2>& _rRhs) const
{
	CTuple<bool, TSize> Result;
	typedef Meta::Min<TSize,TSize2> MinSize;
	
	for (uint32 i(0u); i<MinSize::Result; ++i)
		Result[i] = (m_Values[i] < _rRhs[i]);
	
	for (uint32 i(MinSize::Result); i<TSize; ++i)
		Result[i] = false;
	
	return Result;
}
	
// ---------------------------------------------------------------------------------------------

template <typename TType, uint32 TSize>
template <typename TType2, uint32 TSize2>
CB_FORCEINLINE CTuple<bool, TSize> CB_INLINE_ATTR TUPLE_TYPE::operator <= (const CTuple<TType2, TSize2>& _rRhs) const
{
	CTuple<bool, TSize> Result;
	typedef Meta::Min<TSize,TSize2> MinSize;
	
	for (uint32 i(0u); i<MinSize::Result; ++i)
		Result[i] = (m_Values[i] <= _rRhs[i]);
	
	for (uint32 i(MinSize::Result); i<TSize; ++i)
		Result[i] = false;
	
	return Result;
}
	
// ---------------------------------------------------------------------------------------------

template <typename TType, uint32 TSize>
template <typename TType2, uint32 TSize2>
CB_FORCEINLINE CTuple<bool, TSize> CB_INLINE_ATTR TUPLE_TYPE::operator > (const CTuple<TType2, TSize2>& _rRhs) const
{
	CTuple<bool, TSize> Result;
	typedef Meta::Min<TSize,TSize2> MinSize;
	
	for (uint32 i(0u); i<MinSize::Result; ++i)
		Result[i] = (m_Values[i] > _rRhs[i]);
	
	for (uint32 i(MinSize::Result); i<TSize; ++i)
		Result[i] = true;
	
	return Result;
}
	
// ---------------------------------------------------------------------------------------------
	
template <typename TType, uint32 TSize>
template <typename TType2, uint32 TSize2>
CB_FORCEINLINE CTuple<bool, TSize> CB_INLINE_ATTR TUPLE_TYPE::operator >= (const CTuple<TType2, TSize2>& _rRhs) const
{
	CTuple<bool, TSize> Result;
	typedef Meta::Min<TSize,TSize2> MinSize;
	
	for (uint32 i(0u); i<MinSize::Result; ++i)
		Result[i] = (m_Values[i] >= _rRhs[i]);
	
	for (uint32 i(MinSize::Result); i<TSize; ++i)
		Result[i] = true;
	
	return Result;
}

// ---------------------------------------------------------------------------------------------

template <typename TType, uint32 TSize>
CB_FORCEINLINE bool CB_INLINE_ATTR TUPLE_TYPE::AlmostEq(const TUPLE_TYPE& _rRhs, float _Epsilon) const
{
	for (uint32 i(0u); i<TSize; ++i)
		if (Abs(m_Values[i] - _rRhs[i]) > _Epsilon)
			return false;
	
	return true;
}

// ---------------------------------------------------------------------------------------------

template <typename TType, uint32 TSize>
TType TUPLE_TYPE::LengthSqr() const
{
	return Math::Dot(*this, *this);
}
	
// ---------------------------------------------------------------------------------------------
	
template <typename TType, uint32 TSize>
CB_FORCEINLINE TType CB_INLINE_ATTR TUPLE_TYPE::Length() const
{
	return Sqrt(LengthSqr());
}

// ---------------------------------------------------------------------------------------------

template <typename TType, uint32 TSize>
CB_FORCEINLINE void CB_INLINE_ATTR TUPLE_TYPE::Normalize()
{
	*this /= Length();
}

// ---------------------------------------------------------------------------------------------

template <typename TType, uint32 TSize>
CB_FORCEINLINE std::ostream& CB_INLINE_ATTR operator << (std::ostream& _rLhs, TUPLE_TYPE& _rRhs)
{
	_rLhs << "(";
	for (int i(0); i<TSize; ++i)
	{
		if (i>0) _rLhs << ", ";
		_rLhs << _rRhs[i];
	}
	
	return _rLhs << ")";
}


// -------------------------------------------------------------------------------------------------
//  Utility functions
// -------------------------------------------------------------------------------------------------

template <typename TType, uint32 TSize>
CB_FORCEINLINE TUPLE_TYPE CB_INLINE_ATTR Sqrt(TUPLE_TYPE _Value)
{
	for (uint32 i(0u); i<TSize; ++i)
		_Value[i] = Sqrt(_Value[i]);
	
	return _Value;
}

// -------------------------------------------------------------------------------------------------

template <typename TType, uint32 TSize>
CB_FORCEINLINE TUPLE_TYPE CB_INLINE_ATTR Floor(TUPLE_TYPE _Value)
{
	for (uint32 i(0u); i<TSize; ++i)
		_Value[i] = floor(_Value[i]);
	
	return _Value;
}

// -------------------------------------------------------------------------------------------------

template <typename TType, typename TType2, uint32 TSize>
CB_FORCEINLINE TUPLE_TYPE CB_INLINE_ATTR Clamp (TUPLE_TYPE _Value,
														  const TUPLE_TYPE& _rMin,
														  const CTuple<TType2, TSize>& _rMax)
{
	for (uint32 i(0u); i<TSize; ++i)
		_Value[i] = Math::Clamp(_Value[i], _rMin[i], _rMax[i]);
	
	return _Value;
}

// -------------------------------------------------------------------------------------------------

template <typename TType, uint32 TSize>
CB_FORCEINLINE TUPLE_TYPE CB_INLINE_ATTR Normalize(TUPLE_TYPE _Value)
{
	_Value.Normalize();
	return _Value;
}

// -------------------------------------------------------------------------------------------------

template <typename TType, typename TType2, uint32 TSize>
CB_FORCEINLINE TType CB_INLINE_ATTR Dot(const TUPLE_TYPE& _rLhs,
										const CTuple<TType2, TSize>& _rRhs)
{
	TType ReturnValue(0);
	for (uint32 i(0u); i<TSize; ++i)
		ReturnValue += _rLhs[i] * _rRhs[i];
	
	return ReturnValue;
}

// -------------------------------------------------------------------------------------------------

//! \brief	Calculates a 3-Component cross product and sets the 4th value to the 4th value of the
//!			left-hand-side parameter.
template <>
CB_FORCEINLINE CTuple<float, 4> CB_INLINE_ATTR Cross(const CTuple<float, 4>& _rLhs,
													 const CTuple<float, 4>& _rRhs)
{
	CTuple<float, 4> Result;
	
	Result[0] = (_rLhs[1] * _rRhs[2]) - (_rLhs[2] * _rRhs[1]);
	Result[1] = (_rLhs[2] * _rRhs[0]) - (_rLhs[0] * _rRhs[2]);
	Result[2] = (_rLhs[0] * _rRhs[1]) - (_rLhs[1] * _rRhs[0]);
	Result[3] = _rLhs[3];
	
	return Result;
}

// -------------------------------------------------------------------------------------------------

template <>
CB_FORCEINLINE CTuple<float, 3> CB_INLINE_ATTR Cross(const CTuple<float, 3>& _rLhs,
													 const CTuple<float, 3>& _rRhs)
{
	CTuple<float, 3> Result;
	Result[0] = (_rLhs[1] * _rRhs[2]) - (_rLhs[2] * _rRhs[1]);
	Result[1] = (_rLhs[2] * _rRhs[0]) - (_rLhs[0] * _rRhs[2]);
	Result[2] = (_rLhs[0] * _rRhs[1]) - (_rLhs[1] * _rRhs[0]);
	
	return Result;
}

// -------------------------------------------------------------------------------------------------
//  Operators
// -------------------------------------------------------------------------------------------------

template <typename TType, typename TType2, uint32 TSize>
CB_FORCEINLINE TUPLE_TYPE CB_INLINE_ATTR operator + (TUPLE_TYPE _Lhs,
															   const CTuple<TType2, TSize>& _rRhs)
{
	return _Lhs += _rRhs;
}

// -------------------------------------------------------------------------------------------------

template <typename TType, typename TType2, uint32 TSize>
CB_FORCEINLINE TUPLE_TYPE CB_INLINE_ATTR operator - (TUPLE_TYPE _Lhs,
															   const CTuple<TType2, TSize>& _rRhs)
{
	return _Lhs -= _rRhs;
}

// -------------------------------------------------------------------------------------------------

template <typename TType, typename TType2, uint32 TSize>
CB_FORCEINLINE TUPLE_TYPE CB_INLINE_ATTR operator * (TUPLE_TYPE _Lhs,
															   const CTuple<TType2, TSize>& _rRhs)
{
	return _Lhs *= _rRhs;
}

// -------------------------------------------------------------------------------------------------

template <typename TType, typename TType2, uint32 TSize>
CB_FORCEINLINE TUPLE_TYPE CB_INLINE_ATTR operator / (TUPLE_TYPE _Lhs,
															   const CTuple<TType2, TSize>& _rRhs)
{
	return _Lhs /= _rRhs;
}

// -------------------------------------------------------------------------------------------------

#ifdef CBL_MATRIX_COL_MAJOR
template <typename TVecType, typename TMtxType, uint32 TRowSize, uint32 TColSize>
CB_FORCEINLINE CTuple<TVecType, TRowSize> CB_INLINE_ATTR operator * (const CTuple<TVecType, TRowSize>& _rLhs, const CMatrix<TMtxType, TColSize, TRowSize>& _rRhs)
{
	CTuple<TVecType, TRowSize> Result;
	for (uint32 r(0); r<TRowSize; ++r)
	{
		Result[r] = 0;
		for (uint32 c(0); c<TColSize; ++c)
		{
			Result[r] += _rRhs[c][r] * _rLhs[c];
		}
	}
	
	return Result;
}
#else
template <typename TVecType, typename TMtxType, uint32 TRowSize, uint32 TColSize>
CB_FORCEINLINE CTuple<TVecType, TColSize> CB_INLINE_ATTR operator * (const CTuple<TVecType, TColSize>& _rLhs, const CMatrix<TMtxType, TRowSize, TColSize>& _rRhs)
{
	CTuple<TVecType, TColSize> Result;
	for (uint32 c(0); c<TColSize; ++c)
	{
		Result[c] = 0;
		for (uint32 r(0); r<TRowSize; ++r)
		{
			Result[c] += _rRhs[r][c] * _rLhs[r];
		}
	}
	
	return Result;
}
#endif

// -------------------------------------------------------------------------------------------------

template <typename TType, uint32 TSize>
CB_FORCEINLINE bool CB_INLINE_ATTR All(const TUPLE_TYPE& _rVector, uint32 _Size)
{
	for (uint32 i(0u); i<_Size; ++i)
		if (!_rVector[i])
			return false;
	
	return true;
}

// -------------------------------------------------------------------------------------------------

template <typename TType, uint32 TSize>
CB_FORCEINLINE bool CB_INLINE_ATTR Any(const TUPLE_TYPE& _rVector, uint32 _Size)
{
	for (uint32 i(0u); i<_Size; ++i)
		if (_rVector[i])
			return true;
	
	return false;
}

// -------------------------------------------------------------------------------------------------

template <typename TType, uint32 TSize>
CB_FORCEINLINE bool CB_INLINE_ATTR None(const TUPLE_TYPE& _rVector, uint32 _Size)
{
	for (uint32 i(0u); i<_Size; ++i)
		if (_rVector[i])
			return false;
	
	return true;
}

#undef TUPLE_TYPE

#endif
#endif